using UnityEngine;
using System.Collections;
using System;
using System.Threading;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

public class JFSocket {
	
	//client object of socket
	private Socket clientSocket;
	
	public List<JFPackage.WorldPackage> worldpackage;
	
	//Singleton
	private static JFSocket instance;
	
	public static JFSocket GetInstance(){
        if (instance == null){
            instance = new JFSocket();
        }
        return instance;
    }
	
	JFSocket() {
		//Create socket object,connect type is TCP
		clientSocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
		
		//IP address of Server
		IPAddress ipAddress = IPAddress.Parse ("192.168.1.201");
		
		//IP port of Server
		IPEndPoint ipEndpoint = new IPEndPoint (ipAddress, 808);
		
		//Try an asynchronous connect,if the connect is OK,call connectCallback.
		IAsyncResult result = clientSocket.BeginConnect (ipEndpoint,new AsyncCallback (connectCallback),clientSocket);
		//Wait for 5s,if the connect is still failed,alert("Time is Out").
		bool success = result.AsyncWaitHandle.WaitOne( 5000, true );
		
		if(!success){
			//Time Out
			//Closed();
            Debug.Log("connect Time Out");
		} else {
			//Began to receive data from the server
            worldpackage = new List<JFPackage.WorldPackage>();
            Thread thread = new Thread(new ThreadStart(ReceiveSorket));
            thread.IsBackground = true;
            thread.Start();
		}
	}
	
	private void connectCallback(IAsyncResult asyncConnect){
        Debug.Log("connectSuccess");
    }
	
	private void ReceiveSorket(){
		//receive data in this thread
		while(true){
			if(!clientSocket.Connected){
				//Disconnect to server
				Debug.Log("Failed to clientSocket server.");
                clientSocket.Close();
                break;
			}
			try
			{
				byte[] bytes = new byte[4096];
				int i = clientSocket.Receive(bytes);
                if(i <= 0){
                    clientSocket.Close();
                    break;
                }
				
				//check these data
				//TODO
                if(bytes.Length > 2){
                    SplitPackage(bytes,0);
                }else{
                    Debug.Log("length is not  >  2");
                }
			}
			catch(Exception e)
			{
				Debug.Log("Failed to clientSocket error." + e);
                clientSocket.Close();
                break;
			}
		}
	}
	
	private void SplitPackage(byte[] bytes , int index)
    {
		/*
        //在这里进行拆包，因为一次返回的数据包的数量是不定的
        //所以需要给数据包进行查分。
        while(true)
        {
            //包头是2个字节
            byte[] head = new byte[2];
            int headLengthIndex = index + 2;
            //把数据包的前两个字节拷贝出来
            Array.Copy(bytes,index,head,0,2);
            //计算包头的长度
            short length = BitConverter.ToInt16(head,0);
            //当包头的长度大于0 那么需要依次把相同长度的byte数组拷贝出来
            if(length > 0)
            {
                byte[] data = new byte[length];
                //拷贝出这个包的全部字节数
                Array.Copy(bytes,headLengthIndex,data,0,length);
                //把数据包中的字节数组强制转换成数据包的结构体
                //BytesToStruct()方法就是用来转换的
                //这里需要和你们的服务端程序商量，
                JFPackage.WorldPackage wp = new JFPackage.WorldPackage();
                wp = (JFPackage.WorldPackage)BytesToStruct(data,wp.GetType());
                //把每个包的结构体对象添加至链表中。
                worldpackage.Add(wp);
                //将索引指向下一个包的包头
                index  =  headLengthIndex + length;
 
            }else
            {
                //如果包头为0表示没有包了，那么跳出循环
                break;
            }
        }
        */
    }
	
    public void SendMessage(string str)
    {
        byte[] msg = Encoding.UTF8.GetBytes(str);
 
        if(!clientSocket.Connected)
        {
            clientSocket.Close();
            return;
        }
        try
        {
            //int i = clientSocket.Send(msg);
            IAsyncResult asyncSend = clientSocket.BeginSend (msg,0,msg.Length,SocketFlags.None,new AsyncCallback (sendCallback),clientSocket);
            bool success = asyncSend.AsyncWaitHandle.WaitOne( 5000, true );
            if ( !success )
            {
                clientSocket.Close();
                Debug.Log("Failed to SendMessage server.");
            }
        }
        catch
        {
             Debug.Log("send message error" );
        }
    }
 
    public void SendMessage(object obj)
    {
 
        if(!clientSocket.Connected)
        {
            clientSocket.Close();
            return;
        }
        try
        {
            short size = (short)Marshal.SizeOf(obj);
            //
            byte [] head = BitConverter.GetBytes(size);
            //
            byte[] data = StructToBytes(obj);
 
            byte[] newByte = new byte[head.Length + data.Length];
            Array.Copy(head,0,newByte,0,head.Length);
            Array.Copy(data,0,newByte,head.Length, data.Length);
 
            //
            int length = Marshal.SizeOf(size) + Marshal.SizeOf(obj);
 
            //
            IAsyncResult asyncSend = clientSocket.BeginSend (newByte,0,length,SocketFlags.None,new AsyncCallback (sendCallback),clientSocket);
            //
            bool success = asyncSend.AsyncWaitHandle.WaitOne( 5000, true );
            if ( !success )
            {
                clientSocket.Close();
                Debug.Log("Time Out !");
            }
 
        }
        catch (Exception e)
        {
             Debug.Log("send message error: " + e );
        }
    }
 
    //
    public byte[] StructToBytes(object structObj)
    {
 
        int size = Marshal.SizeOf(structObj);
        IntPtr buffer =  Marshal.AllocHGlobal(size);
        try
        {
            Marshal.StructureToPtr(structObj,buffer,false);
            byte[]  bytes  =   new byte[size];
            Marshal.Copy(buffer, bytes,0,size);
            return   bytes;
        }
        finally
        {
            Marshal.FreeHGlobal(buffer);
        }
    }
    //
    public object BytesToStruct(byte[] bytes,   Type   strcutType)
    {
        int size = Marshal.SizeOf(strcutType);
        IntPtr buffer = Marshal.AllocHGlobal(size);
        try
        {
            Marshal.Copy(bytes,0,buffer,size);
            return  Marshal.PtrToStructure(buffer,   strcutType);
        }
        finally
        {
            Marshal.FreeHGlobal(buffer);
        }  
 
    }
 
    private void sendCallback (IAsyncResult asyncSend)
    {
 
    }
 
    //
    public void Closed()
    {
 
        if(clientSocket != null && clientSocket.Connected)
        {
            clientSocket.Shutdown(SocketShutdown.Both);
            clientSocket.Close();
        }
        clientSocket = null;
    }
 
}
